package top.luoyuanxiang.mqtt.utils;

import cn.hutool.core.util.StrUtil;
import jakarta.validation.constraints.NotNull;
import lombok.experimental.UtilityClass;

/**
 * 二进制处理工具类
 *
 * @author luoyuanxiang
 */
@UtilityClass
public class BinaryUtil {

    /**
     * 计算从消息头开始到校验码前一个字节的异或校验和
     *
     * @param bytes 字节流
     * @return 校验和（单个字节）
     */
    public byte calculateChecksum(byte[] bytes) {
        byte checksum = (byte) 0;
        for (byte b : bytes) {
            checksum ^= b;
        }
        return checksum;
    }

    /**
     * 验证字节数组中的最后一个字节是否与前n个字节的异或校验码匹配
     *
     * @param bytesWithChecksum 带校验和字节数
     * @return boolean
     */
    public boolean verifyChecksum(@NotNull byte[] bytesWithChecksum) {
        byte[] data = new byte[bytesWithChecksum.length - 1];
        System.arraycopy(bytesWithChecksum, 0, data, 0, data.length);
        byte receivedChecksum = bytesWithChecksum[bytesWithChecksum.length - 1];
        byte calculatedChecksum = calculateChecksum(data);
        return receivedChecksum == calculatedChecksum;
    }


    /**
     * 合并两个或更多的byte数组，返回新的数组且不改变原数组内容。
     *
     * @param arrays 要合并的byte数组
     * @return 合并后的新byte数组
     */
    public byte[] mergeArrays(@NotNull byte[]... arrays) {
        int totalLength = 0;
        for (byte[] array : arrays) {
            totalLength += array.length;
        }
        // 创建一个新数组来存储合并的结果
        byte[] mergedArray = new byte[totalLength];
        int offset = 0;
        // 将每个数组的内容复制到新数组中
        for (byte[] array : arrays) {
            System.arraycopy(array, 0, mergedArray, offset, array.length);
            offset += array.length;
        }
        return mergedArray;
    }

    /**
     * 截取byte数组的一部分，并返回一个新的byte数组，不改变原数组内容。
     *
     * @param original   原始的byte数组
     * @param startIndex 开始截取的索引（包含该位置）
     * @param length     截取的长度
     * @return 新的截取后的byte数组
     */
    public byte[] subArray(byte[] original, int startIndex, int length) {
        if (startIndex < 0 || startIndex >= original.length || length <= 0 || startIndex + length > original.length) {
            throw new IllegalArgumentException("Invalid start index or length");
        }
        // 创建一个新的byte数组来存储截取的结果
        byte[] subArray = new byte[length];
        // 使用System.arraycopy()方法复制部分数据
        System.arraycopy(original, startIndex, subArray, 0, length);
        return subArray;
    }

    /**
     * 去掉消息头处理
     *
     * @param hexStr 十六进制 str
     */
    public byte[] removeMessageHeader(String hexStr) {
        // 判断消息是否包含标识位7e
        if (!isCheck(hexStr)) {
            throw new RuntimeException("消息不合法，请检查！");
        }
        // 去掉标识位
        hexStr = hexStr.replace("7e", "");
        // 解码
        hexStr = Escape_0x7e.decode(hexStr);

        byte[] dataByte = Jtt808Utils.hex2byte(hexStr);
        // 校验位校验
        if (!BinaryUtil.verifyChecksum(dataByte)) {
            byte[] bytes = BinaryUtil.subArray(dataByte, 0, dataByte.length - 1);
            byte calculated = BinaryUtil.calculateChecksum(bytes);
            throw new RuntimeException("校验位校验失败, 校验位：" + Jtt808Utils.byte2Hex(new byte[]{calculated}) + "，实际校验位：" + Jtt808Utils.byte2Hex(new byte[]{dataByte[dataByte.length - 1]}));
        }
        // 去掉消息头
        dataByte = BinaryUtil.subArray(dataByte, 17, dataByte.length - 17);
        return dataByte;
    }

     /**
     * 判断消息是否合法
     *
     * @param hexData 十六进制数据
     * @return boolean
     */
    public boolean isCheck(String hexData) {
        if (StrUtil.isBlank(hexData)) {
            return false;
        }
        return hexData.contains(Escape_0x7e.HEX_FRAME_FLAG.toLowerCase());
    }

    /**
     * 获取消息 ID
     *
     * @param data 数据
     * @return {@link String }
     */
    public String getMessageId(byte[] data) {
        return Jtt808Utils.byte2Hex(BinaryUtil.subArray(data, 0, 2)).toUpperCase();
    }
}
